//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
// (c) Nikita Karnauhov (jinx@navitel.su), 2007
//
// Paste the clipboard data to the map.
//

# include "StdAfx.h"
# include "Paste.h"
# include "UndoRedo.h"
# include "AddMap.h"
# include "Clipboard.h"
# include "Selection.h"
# include "Types.h"
# include "Scale.h"
# include "STL_helpers.h"
# include "Globals.h"

class CPasteAction : public CEditAction {
  public:
	CPasteAction (map_t * _pMap, bool _bPAIsUsed, bool _bRGNodesUsed) :
		m_pMap (_pMap),
		m_bPAIsUsed (_bPAIsUsed),
		m_bRGNodesUsed (_bRGNodesUsed)
	{}

	virtual void GetName (string_t & _str) const {_str.append ("paste");}
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	map_t * m_pMap;
	std::vector<rgn_t *> m_RgnsCopied;

	bool m_bPAIsUsed;
	struct pai_data_t {
		pa_item_t * p;
		bool bWasDeleted;
	};
	std::vector<pai_data_t> m_PAIsCopied;

	bool m_bRGNodesUsed;
	struct rgnode_data_t {
		rgnode_t * p;
	};
	std::vector<rgnode_data_t> m_RGNodesCopied;
};

bool CPasteAction::Do () {
	assert (m_pMap);

	CCritSec::CLock lock (g_cs);

	//
	// Paste postal address items.
	//

	if (m_bPAIsUsed) {
		const size_t cCountries = m_pMap->Countries.size ();
		const size_t cRegions   = m_pMap->Regions  .size ();
		const size_t cCities    = m_pMap->Cities   .size ();
		const size_t cZips      = m_pMap->Zips     .size ();
		m_PAIsCopied.resize (cCountries + cRegions + cCities + cZips);
		size_t c = 0;
		for (countries_t::iterator ci = m_pMap->Countries.begin (); ci  != m_pMap->Countries.end (); ++ ci)
			m_PAIsCopied [c ++].p = & (* ci);
		for (regions_t::iterator   ri = m_pMap->Regions  .begin (); ri  != m_pMap->Regions  .end (); ++ ri)
			m_PAIsCopied [c ++].p = & (* ri);
		for (cities_t::iterator   cti = m_pMap->Cities   .begin (); cti != m_pMap->Cities   .end (); ++ cti)
			m_PAIsCopied [c ++].p = & (* cti);
		for (zips_t::iterator      zi = m_pMap->Zips     .begin (); zi  != m_pMap->Zips     .end (); ++ zi)
			m_PAIsCopied [c ++].p = & (* zi);

		JoinLists (g_map.Countries, m_pMap->Countries);
		JoinLists (g_map.Regions,   m_pMap->Regions);
		JoinLists (g_map.Cities,    m_pMap->Cities);
		JoinLists (g_map.Zips,      m_pMap->Zips);
	}

	if (m_bRGNodesUsed) {
		m_RGNodesCopied.resize (m_pMap->rgnodes.size ());
		size_t c = 0;
		for (rgnodes_t::iterator ni = m_pMap->rgnodes.begin (); ni != m_pMap->rgnodes.end (); ++ ni)
			m_RGNodesCopied [c ++].p = & (* ni);

		JoinLists     (g_map.rgnodes,        m_pMap->rgnodes);
		JoinMultimaps (g_map.rgrestrictions, m_pMap->rgrestrictions);
	}

	//
	// Paste objects.
	//

	m_RgnsCopied.reserve (m_pMap->GetRgnCount ());
	for (size_t cKind = 0; cKind < rkMax; ++ cKind) {
		layer_t & layerTo   = g_map.layers   [cKind];
		layer_t & layerFrom = m_pMap->layers [cKind];

		if (layerFrom.cRgns != 0) {
			// Save pointers to the added objects.
			rgns_t & rgns = layerFrom.rgns;
			for (rgns_t::iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
				rgn_t & rgn = * iRgn;
				assert (! rgn.IsDeleted ());
				m_RgnsCopied.push_back (& rgn);
			}

			// Splice list of objects in the layer.
			layerTo.rgns.splice (layerTo.rgns.begin (), layerFrom.rgns);
			layerTo.cRgns        += layerFrom.cRgns;
			layerTo.cDeletedRgns += layerFrom.cDeletedRgns;

			if (cKind == rkPolygon)
				g_map.RefreshDrawOrder ();
		}
	}

	if (g_map.cMaxPointsInElement < m_pMap->cMaxPointsInElement)
		g_map.cMaxPointsInElement = m_pMap->cMaxPointsInElement;

	g_map.rectBound.Extend (m_pMap->rectBound);

	g_map.SpliceSaveLists (* m_pMap);

	// NOTE: PAI duplicates removal should be done AFTER joining objects lists.
	if (m_bPAIsUsed)
		g_map.RemovePAIDuplicates ();

	if (m_bRGNodesUsed) {
		g_map.CountRGNodes ();
		g_map.MakeSureAllNodeIDsAreUnique ();
		g_map.MakeSureAllRoadIDsAreUnique ();
	}

	m_pMap = NULL;

	return true;
}

void CPasteAction::Undo () {
	CCritSec::CLock lock (g_cs);

	const size_t cRgns = m_RgnsCopied.size ();
	for (size_t cRgn = 0; cRgn < cRgns; ++ cRgn)
		m_RgnsCopied [cRgn]->Delete (g_map);

	if (m_bPAIsUsed) {
		const size_t cPAIsCopied = m_PAIsCopied.size ();
		for (size_t c = 0; c < cPAIsCopied; ++ c) {
			pai_data_t & data = m_PAIsCopied [c];
			data.bWasDeleted = data.p->bDeleted;
			data.p->bDeleted = true;
		}
	}
}

void CPasteAction::Redo () {
	CCritSec::CLock lock (g_cs);

	const size_t cRgns = m_RgnsCopied.size ();
	for (size_t cRgn = 0; cRgn < cRgns; ++ cRgn)
		m_RgnsCopied [cRgn]->Undelete (g_map);

	if (m_bPAIsUsed) {
		const size_t cPAIsCopied = m_PAIsCopied.size ();
		for (size_t c = 0; c < cPAIsCopied; ++ c) {
			pai_data_t & data = m_PAIsCopied [c];
			data.p->bDeleted = data.bWasDeleted;
		}
	}
}

///////////////////////////////////////

bool IsClipboardEmpty () {
	return ! ::IsClipboardFormatAvailable (g_uiClipboardFormat);
}

template <typename T> inline
const T & GetData (const BYTE * _pData, size_t & _cOffset) {
	const T & ret = * reinterpret_cast<const T *> (_pData + _cOffset);
	_cOffset += sizeof (T);

	return ret;
}

void GetData (const BYTE * _pData, size_t & _cOffset, string_t & _ret) {
	size_t cLen = * reinterpret_cast<const size_t *> (_pData + _cOffset);
	_cOffset += sizeof (size_t);

	_ret.assign (reinterpret_cast<const char *> (_pData + _cOffset), cLen);
	_cOffset += cLen*sizeof (char);
}

void EditPaste () {
	//
	// Initialize access to the clipboard.
	//

	CClipboard cb;

	const HGLOBAL hGlobal = ::GetClipboardData (g_uiClipboardFormat);
	if (! hGlobal) {
		ErrorMsgBox ("Can't paste data of this type.");
		return;
	}

	const BYTE * const pData = reinterpret_cast<BYTE *> (::GlobalLock (hGlobal));
	size_t cOffset = 0;

	const size_t cDataSize = GetData<size_t> (pData, cOffset);
	if (::GlobalSize (hGlobal) < cDataSize) {
		ErrorMsgBox ("Data are damaged.");
		return;
	}

	//
	// Parse the data.
	//

	g_Selection.clear ();

	map_t map;

	// Parse type set.
	string_t strTypeSet;
	GetData (pData, cOffset, strTypeSet);
	map.pTypeSet = CTypeSet::FindTypeSet (strTypeSet.c_str ());

	// Parse header.
	map.Header.ElevationUnits = static_cast<elevation_units_t> (GetData<BYTE> (pData, cOffset));
	map.Header.wCodePage      =                                 GetData<WORD> (pData, cOffset);
	map.Header.CodeSchema     = static_cast<CCodeSchema_t>     (GetData<BYTE> (pData, cOffset));

	// Parse levels vector.
	const size_t cLevels = GetData<size_t> (pData, cOffset);
	map.Levels.resize (cLevels);
	for (size_t cLevel = 0; cLevel < cLevels; ++ cLevel) {
		map_level_t & level = map.Levels [cLevel];
		level.btBits        = GetData<size_t> (pData, cOffset);
		level.btBitsRangeTo = GetData<size_t> (pData, cOffset);
		level.cZoom         = GetData<size_t> (pData, cOffset);
	}
	map.bNoHeader = false;

	// Paste countries.
	const size_t cCountries = GetData<size_t> (pData, cOffset);
	std::vector<country_t *> countries (cCountries);
	for (size_t cCountry = 0; cCountry < cCountries; ++ cCountry) {
		map.Countries.push_back (country_t ());
		country_t & country = map.Countries.back ();
		GetData (pData, cOffset, country.strName);
		GetData (pData, cOffset, country.strAbbrevName);

		countries [cCountry] = & country;
	}

	// Paste regions.
	const size_t cRegions = GetData<size_t> (pData, cOffset);
	std::vector<region_t *> regions (cRegions);
	for (size_t cRegion = 0; cRegion < cRegions; ++ cRegion) {
		map.Regions.push_back (region_t ());
		region_t & region = map.Regions.back ();

		GetData (pData, cOffset, region.strName);
		GetData (pData, cOffset, region.strAbbrevName);

		const size_t cCountryIdx = GetData<size_t> (pData, cOffset);
		region.pCountry = cCountryIdx < cCountries ? countries [cCountryIdx] : 0;

		regions [cRegion] = & region;
	}

	// Paste cities.
	const size_t cCities = GetData<size_t> (pData, cOffset);
	std::vector<city_t *> cities (cCities);
	for (size_t cCity = 0; cCity < cCities; ++ cCity) {
		map.Cities.push_back (city_t ());
		city_t & city = map.Cities.back ();

		GetData (pData, cOffset, city.strName);

		const size_t cRegionIdx = GetData<size_t> (pData, cOffset);
		city.pRegion = cRegionIdx < cRegions ? regions [cRegionIdx] : 0;

		cities [cCity] = & city;
	}

	// Paste zips.
	const size_t cZips = GetData<size_t> (pData, cOffset);
	std::vector<zip_t *> zips (cZips);
	for (size_t cZip = 0; cZip < cZips; ++ cZip) {
		map.Zips.push_back (zip_t ());
		zip_t & zip = map.Zips.back ();
		GetData (pData, cOffset, zip.strName);

		zips [cZip] = & zip;
	}

	// Paste RG nodes.
	const size_t cRGNodes = GetData<size_t> (pData, cOffset);
	std::vector<rgnode_t *> RGNodes (cRGNodes);
	for (size_t cRGNode = 0; cRGNode < cRGNodes; ++ cRGNode) {
		const DWORD dwNodeID     = GetData<DWORD> (pData, cOffset);
		const BYTE  btIsExternal = GetData<BYTE>  (pData, cOffset);

		rgnode_t & rgnode = map.CreateNode (dwNodeID);
		rgnode.bIsExternal = btIsExternal != 0;

		RGNodes [cRGNode] = & rgnode;

		map.bDirtyRGNodes = true;
	}

	// Parse objects.
	bool bPAIsUsed = false;
	bool bRGNodesUsed = false;
	const size_t cRgns = GetData<size_t> (pData, cOffset);
	std::vector<rgn_t *> vecRgns (cRgns);
	for (size_t cRgn = 0; cRgn < cRgns; ++ cRgn) {
		const USHORT ushType    = GetData<USHORT> (pData, cOffset);
		const BYTE   btKind     = GetData<BYTE>   (pData, cOffset);
		const BYTE   btEndLevel = GetData<BYTE>   (pData, cOffset);
		const BYTE   btFlags    = GetData<BYTE>   (pData, cOffset);

		const rgns_t::iterator iRgn = map.CreateRgnIt ((RGN_KIND) btKind);
		rgn_t & rgn = * iRgn;

		vecRgns [cRgn] = & rgn;

		// Initialize the object.
		rgn.dwFlags |= rgn_t::fModified;

		rgn.SetType (map.pTypeSet, ushType, btKind);

		rgn.dwFlags     = btFlags;
		rgn.uchEndLevel = btEndLevel;

		GetData (pData, cOffset, rgn.strLabel);
		GetData (pData, cOffset, rgn.strComment);

		string_t strExtra;
		GetData (pData, cOffset, strExtra);
		if (! strExtra.empty ())
			rgn.pExtra->strExtra.swap (strExtra);

		const size_t cCityIdx = GetData<size_t> (pData, cOffset);
		if (cCityIdx < cities.size ()) {
			rgn.pExtra->pCity = cities [cCityIdx];
			bPAIsUsed = true;
		}

		const size_t cZipIdx  = GetData<size_t> (pData, cOffset);
		if (cZipIdx < zips.size ()) {
			rgn.pExtra->pZip = zips [cZipIdx];
			bPAIsUsed = true;
		}

		string_t strStreet, strNumber, strPhone, strFax, strEmail, strWebPage, strDescription;
		GetData (pData, cOffset, strStreet);
		GetData (pData, cOffset, strNumber);
		GetData (pData, cOffset, strPhone);
		GetData (pData, cOffset, strFax);
		GetData (pData, cOffset, strEmail);
		GetData (pData, cOffset, strWebPage);
		GetData (pData, cOffset, strDescription);
		if (! strStreet.empty ())
			rgn.pExtra->strStreetDesc.swap (strStreet);
		if (! strNumber.empty ())
			rgn.pExtra->strNumber.swap (strNumber);
		if (! strPhone.empty ())
			rgn.pExtra->strPhone.swap (strPhone);
		if (! strFax.empty ())
			rgn.pExtra->strFax.swap (strFax);
		if (! strEmail.empty ())
			rgn.pExtra->strEmail.swap (strEmail);
		if (! strWebPage.empty ())
			rgn.pExtra->strWebPage.swap (strWebPage);
		if (! strDescription.empty ())
			rgn.pExtra->strDescription.swap (strDescription);

		const DWORD dwRouteParams = GetData<DWORD> (pData, cOffset);
		if (dwRouteParams != 0)
			rgn.pExtra->dwRouteParams = dwRouteParams;

		const size_t cElements = GetData<size_t> (pData, cOffset);

		if (btKind == rkPoint) {
			rgn.origins.resize (cElements);
			for (size_t cOrigin = 0; cOrigin < cElements; ++ cOrigin) {
				rgn_origin_t & origin = rgn.origins [cOrigin];

				origin.cFileOffset = -1;
				
				origin.cLevel = GetData<BYTE>    (pData, cOffset);
				origin.point  = GetData<point_t> (pData, cOffset);
			}
		} else {
			assert (btKind == rkPolyline || btKind == rkPolygon);

			rgn.elements.resize (cElements);
			for (size_t cElement = 0; cElement < cElements; ++ cElement) {
				rgn_element_t & element = rgn.elements [cElement];

				element.cFileOffset = -1;
				
				element.cLevel = GetData<BYTE> (pData, cOffset);
				element.bHole  = GetData<BYTE> (pData, cOffset) != 0;

				const size_t cPoints = GetData<size_t> (pData, cOffset);
				points_t & points = element.points;
				points.resize (cPoints);
				for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint)
					points [cPoint] = GetData<point_t> (pData, cOffset);

				if (btKind == rkPolyline) {
					const size_t cPointExtras = GetData<size_t> (pData, cOffset);
					if (cPointExtras != 0) {
						point_extras_t & PointExtras = element.pExtra->PointExtras;
						PointExtras.resize (cPointExtras);

						for (size_t cPoint = 0; cPoint < cPointExtras; ++ cPoint) {
							// RG node.
							const size_t cNodeIdx = GetData<size_t> (pData, cOffset);
							if (cNodeIdx != -1 && cNodeIdx < RGNodes.size ()) {
								PointExtras [cPoint].SetRGNode (RGNodes [cNodeIdx], cPoint, cPoints);
								bRGNodesUsed = true;
							}

							// Numbers.
							numbers_t & Numbers = PointExtras [cPoint].Numbers;
							Numbers.bHasNumbers  = GetData<bool> (pData, cOffset);
							Numbers.wLeftParity  = GetData<BYTE> (pData, cOffset);
							Numbers.wLeftBefore  = GetData<WORD> (pData, cOffset);
							Numbers.wLeftAfter   = GetData<WORD> (pData, cOffset);
							Numbers.wRightParity = GetData<BYTE> (pData, cOffset);
							Numbers.wRightBefore = GetData<WORD> (pData, cOffset);
							Numbers.wRightAfter  = GetData<WORD> (pData, cOffset);

							const size_t cCityLeftIdx  = GetData<size_t> (pData, cOffset);
							const size_t cCityRightIdx = GetData<size_t> (pData, cOffset);
							const size_t cZipLeftIdx   = GetData<size_t> (pData, cOffset);
							const size_t cZipRightIdx  = GetData<size_t> (pData, cOffset);

							if (cCityLeftIdx < cities.size ()) {
								Numbers.pCityLeftAfter = cities [cCityLeftIdx];
								bPAIsUsed = true;
							}
							if (cCityRightIdx < cities.size ()) {
								Numbers.pCityRightAfter = cities [cCityRightIdx];
								bPAIsUsed = true;
							}

							if (cZipLeftIdx < zips.size ()) {
								Numbers.pZipLeftAfter = zips [cZipLeftIdx];
								bPAIsUsed = true;
							}
							if (cZipRightIdx < zips.size ()) {
								Numbers.pZipRightAfter = zips [cZipRightIdx];
								bPAIsUsed = true;
							}
						}
					}
				}
			}
		}

		rgn.RefreshAttributes (map);

		find_info_t fi;
		fi.pLayer           = & map.layers [rgn.Kind];
		fi.iRgn             = iRgn;
		fi.cElementOrOrigin = -1;
		g_Selection.push_back (fi);
	}

	const size_t cRestrictions = GetData<size_t> (pData, cOffset);
	for (size_t cRestriction = 0; cRestriction < cRestrictions; ++ cRestriction) {
		const size_t cNodeIdx      = GetData<DWORD>  (pData, cOffset);
		const size_t cFromIdx      = GetData<size_t> (pData, cOffset);
		const size_t cToIdx        = GetData<size_t> (pData, cOffset);
		const BYTE btFromDirection = GetData<BYTE>   (pData, cOffset);
		const BYTE btToDirection   = GetData<BYTE>   (pData, cOffset);

		if (cNodeIdx >= RGNodes.size ())
			continue;
		rgnode_t * const pNode = RGNodes [cNodeIdx];
		pNode->bHasRestrictions = true;

		rgrestriction_t & restriction = AddRGNodeRestriction (map, pNode);
		restriction.pFromRoad      = cFromIdx < vecRgns.size () ? vecRgns [cFromIdx] : NULL;
		restriction.pToRoad        = cFromIdx < vecRgns.size () ? vecRgns [cToIdx]   : NULL;
		restriction.bFromDirection = btFromDirection;
		restriction.bToDirection   = btToDirection;
	}

	assert (cDataSize == cOffset);
	::GlobalUnlock (hGlobal);

	//
	// Join the maps.
	//

	CCritSec::CLock lock (g_cs);

	if (! CanAddMap (map, g_map)) {
		ErrorMsgBox ("The map data in the clipboard have different type set and can't be pasted here.");
		return;
	}

	const bool bWasEmpty = g_map.empty ();

	if (
		map.Levels                != g_map.Levels ||
		map.Header.ElevationUnits != g_map.Header.ElevationUnits ||
		map.Header.wCodePage      != g_map.Header.wCodePage ||
		map.Header.CodeSchema     != g_map.Header.CodeSchema ||
		map.pTypeSet              != g_map.pTypeSet
	)
		AddMap (map, g_map);
	else
		PerformEditAction (new CPasteAction (& map, bPAIsUsed, bRGNodesUsed));

	if (bWasEmpty) {
		// TODO: do the same as done in the end of Load()?
		SetFullMapScale ();		
	}
}
